#!/usr/bin/env python3
"""
游戏解包与源码查看工具 - 主应用
支持格式：APK、IPA、Unity、Unreal、HTML5
"""

import os
import json
import zipfile
import shutil
import subprocess
from pathlib import Path
from flask import Flask, render_template, request, jsonify, send_file, abort
import uuid
import threading

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'static/uploads'
app.config['EXTRACT_FOLDER'] = 'static/extracted'
app.config['MAX_CONTENT_LENGTH'] = 500 * 1024 * 1024  # 500MB

# 确保目录存在
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['EXTRACT_FOLDER'], exist_ok=True)

class GameUnpacker:
    """游戏解包器核心类"""
    
    @staticmethod
    def detect_game_type(file_path):
        """检测游戏类型"""
        file_path = Path(file_path)
        
        if file_path.suffix.lower() == '.apk':
            return 'APK'
        elif file_path.suffix.lower() == '.ipa':
            return 'IPA'
        elif file_path.suffix.lower() in ['.zip', '.exe']:
            # 检查是否为Unity或Unreal
            try:
                with zipfile.ZipFile(file_path, 'r') as zip_ref:
                    files = zip_ref.namelist()
                    if any('UnityPlayer.dll' in f for f in files):
                        return 'Unity'
                    elif any('UE4Game' in f or 'UnrealEngine' in f for f in files):
                        return 'Unreal'
                    elif any('index.html' in f for f in files):
                        return 'HTML5'
            except:
                pass
        return 'Unknown'
    
    @staticmethod
    def extract_apk(file_path, extract_path):
        """解包APK文件"""
        try:
            with zipfile.ZipFile(file_path, 'r') as zip_ref:
                zip_ref.extractall(extract_path)
            
            # 反编译DEX文件
            dex_files = list(Path(extract_path).glob('*.dex'))
            if dex_files:
                for dex_file in dex_files:
                    subprocess.run(['java', '-jar', 'tools/jadx/bin/jadx', 
                                  str(dex_file), '-d', str(extract_path / 'java_source')], 
                                 capture_output=True)
            return True
        except Exception as e:
            print(f"APK解包失败: {e}")
            return False
    
    @staticmethod
    def extract_zip(file_path, extract_path):
        """通用ZIP解包"""
        try:
            with zipfile.ZipFile(file_path, 'r') as zip_ref:
                zip_ref.extractall(extract_path)
            return True
        except Exception as e:
            print(f"ZIP解包失败: {e}")
            return False
    
    @staticmethod
    def extract_game(file_path, extract_path, game_type):
        """根据游戏类型解包"""
        if game_type == 'APK':
            return GameUnpacker.extract_apk(file_path, extract_path)
        elif game_type in ['IPA', 'Unity', 'Unreal', 'HTML5']:
            return GameUnpacker.extract_zip(file_path, extract_path)
        return False

@app.route('/')
def index():
    """主页"""
    return render_template('index.html')

@app.route('/upload', methods=['POST'])
def upload_file():
    """文件上传"""
    if 'file' not in request.files:
        return jsonify({'error': '没有选择文件'}), 400
    
    file = request.files['file']
    if file.filename == '':
        return jsonify({'error': '没有选择文件'}), 400
    
    # 生成唯一ID
    file_id = str(uuid.uuid4())
    filename = file.filename
    file_path = os.path.join(app.config['UPLOAD_FOLDER'], f"{file_id}_{filename}")
    
    # 保存文件
    file.save(file_path)
    
    # 检测游戏类型
    game_type = GameUnpacker.detect_game_type(file_path)
    
    return jsonify({
        'file_id': file_id,
        'filename': filename,
        'game_type': game_type
    })

@app.route('/extract/<file_id>')
def extract_game(file_id):
    """解包游戏"""
    # 查找上传的文件
    upload_files = [f for f in os.listdir(app.config['UPLOAD_FOLDER']) 
                   if f.startswith(file_id)]
    
    if not upload_files:
        return jsonify({'error': '文件不存在'}), 404
    
    filename = upload_files[0]
    file_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    extract_path = os.path.join(app.config['EXTRACT_FOLDER'], file_id)
    
    # 创建解包目录
    os.makedirs(extract_path, exist_ok=True)
    
    # 检测游戏类型
    game_type = GameUnpacker.detect_game_type(file_path)
    
    # 解包
    success = GameUnpacker.extract_game(file_path, extract_path, game_type)
    
    if success:
        return jsonify({
            'success': True,
            'extract_path': f"/static/extracted/{file_id}",
            'game_type': game_type
        })
    else:
        return jsonify({'error': '解包失败'}), 500

@app.route('/browse/<path:subpath>')
def browse_files(subpath):
    """浏览解包后的文件"""
    base_path = app.config['EXTRACT_FOLDER']
    full_path = os.path.join(base_path, subpath)
    
    if not os.path.exists(full_path):
        abort(404)
    
    if os.path.isfile(full_path):
        # 返回文件内容
        try:
            with open(full_path, 'r', encoding='utf-8') as f:
                content = f.read()
            return jsonify({
                'type': 'file',
                'content': content,
                'path': subpath
            })
        except:
            return jsonify({
                'type': 'file',
                'content': '[二进制文件]',
                'path': subpath
            })
    else:
        # 返回目录列表
        items = []
        for item in os.listdir(full_path):
            item_path = os.path.join(full_path, item)
            items.append({
                'name': item,
                'type': 'directory' if os.path.isdir(item_path) else 'file',
                'path': os.path.join(subpath, item)
            })
        
        return jsonify({
            'type': 'directory',
            'items': sorted(items, key=lambda x: (x['type'] != 'directory', x['name'])),
            'path': subpath
        })

@app.route('/search/<file_id>')
def search_files(file_id):
    """搜索文件内容"""
    query = request.args.get('q', '')
    if not query:
        return jsonify([])
    
    extract_path = os.path.join(app.config['EXTRACT_FOLDER'], file_id)
    results = []
    
    for root, dirs, files in os.walk(extract_path):
        for file in files:
            if file.endswith(('.txt', '.json', '.xml', '.html', '.js', '.css', '.py', '.java', '.cs', '.cpp', '.h')):
                file_path = os.path.join(root, file)
                try:
                    with open(file_path, 'r', encoding='utf-8') as f:
                        content = f.read()
                        if query.lower() in content.lower():
                            relative_path = os.path.relpath(file_path, extract_path)
                            results.append({
                                'file': relative_path,
                                'preview': content[:200] + '...' if len(content) > 200 else content
                            })
                except:
                    continue
    
    return jsonify(results)

@app.route('/run/<file_id>')
def run_game(file_id):
    """在沙盒环境中运行游戏"""
    extract_path = os.path.join(app.config['EXTRACT_FOLDER'], file_id)
    
    # 查找可运行文件
    html_files = list(Path(extract_path).glob('**/*.html'))
    if html_files:
        # HTML5游戏
        html_file = html_files[0]
        relative_path = html_file.relative_to(Path(app.config['EXTRACT_FOLDER']))
        return jsonify({
            'type': 'html5',
            'url': f"/static/extracted/{relative_path}"
        })
    
    return jsonify({'error': '未找到可运行文件'})

@app.route('/report/<file_id>')
def generate_report(file_id):
    """生成分析报告"""
    extract_path = os.path.join(app.config['EXTRACT_FOLDER'], file_id)
    
    report = {
        'file_id': file_id,
        'analysis_date': str(pd.Timestamp.now()) if 'pd' in globals() else 'N/A',
        'structure': {},
        'file_count': 0,
        'directories': []
    }
    
    file_count = 0
    for root, dirs, files in os.walk(extract_path):
        level = root.replace(extract_path, '').count(os.sep)
        indent = ' ' * 2 * level
        report['structure'][root] = {
            'dirs': dirs,
            'files': files,
            'level': level
        }
        file_count += len(files)
    
    report['file_count'] = file_count
    
    return jsonify(report)

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0', port=5000)
